package org.carabiner.infinitest;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;

import org.carabiner.infinitest.depgraph.DependencyGraph;
import org.carabiner.infinitest.depgraph.JavaClass;
import org.carabiner.infinitest.depgraph.TestVerifier;

public class InfinitestMain extends TimerTask
{  
  private Color workingColor;
  private Timer timer;
  private DependencyGraph index;  
  private TestRunner runner;
  private ResultTreeModel testResults;
  private JTree tree;
  private JFrame frame;
  private boolean updateRunning;  

  public InfinitestMain(File path) throws IOException
  {    
    frame = new JFrame();    
    frame.setAlwaysOnTop(true);
    frame.setLayout(new BorderLayout());
    testResults = new ResultTreeModel();
    tree = createTree();
    workingColor = tree.getBackground();
    testResults.addTreeModelListener(new TreeModelExpansionListener(tree));
    frame.add(new JScrollPane(tree), BorderLayout.CENTER);    
    runner = new JUnitRunner();
    runner.addTestStatusListener(new EventNormalizer(testResults));
    runner.addTestStatusListener(new EventNormalizer(new FrameUpdater(frame, tree)));
    TestVerifier v = new TestCaseVerifier();
    index = new DependencyGraph(path, v);    
    timer = new Timer();
    frame.add(createToolBar(), BorderLayout.WEST);
    frame.setTitle("Infinitest - Scanning for tests...");
    frame.setSize(480, 140);    
    timer.schedule(this, 100, InfinitestSettings.getRefreshRate());    
  }

  private Component createToolBar()
  {
    JToolBar toolbar = new JToolBar(JToolBar.VERTICAL);    
    toolbar.add(new ReloadIndexAction(index));    
    toolbar.add(new HaltTestAction());
    return toolbar;
  }

  private JTree createTree()
  {
    JTree resultsTree = new ResultsTree(testResults);
    resultsTree.setRootVisible(false);
    resultsTree.setEditable(false);
    resultsTree.setShowsRootHandles(true);
    resultsTree.setCellRenderer(new FailureCellRenderer());
    ToolTipManager tipManager = ToolTipManager.sharedInstance();
    tipManager.registerComponent(resultsTree);
    tipManager.setDismissDelay(10000);
    return resultsTree;
  }
  
  private URL[] toURLs(File[] path) throws MalformedURLException
  {
    URL[] buildPath = new URL[path.length];
    for(int i = 0; i < path.length; i++)
    {
      buildPath[i] = path[i].toURL();
    }
    return buildPath;
  }

  private Set<String> getNames(Set<JavaClass> testsToRun)
  {
    Set<String> names = new HashSet<String>();
    for(JavaClass clazz : testsToRun)
    {
      names.add(clazz.getName());
    }
    return names;
  }

  JTree getTree()
  {
    return tree;
  }

  JFrame getFrame()
  {
    return frame;
  }

  @Override
  public synchronized void run()
  {
    updateRunning = true;
    try
    {      
      Set<JavaClass> testsToRun = index.update();
      if (!testsToRun.isEmpty())
      {
        tree.setBackground(workingColor);
        setTitle("Infinitest - Running Tests...");
      }      
      for(String testName : getNames(testsToRun))
      {
        runner.runTest(testName);            
      }      
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }    
    finally
    {
      SwingUtilities.invokeLater(new Runnable()
      {
        public void run()
        {
          synchronized(InfinitestMain.this)
          {
            updateRunning = false;
            InfinitestMain.this.notify();          
          }
        }
      });      
    }
  }

  private void setTitle(final String newTitle)
  {
    SwingUtilities.invokeLater(new Runnable()
    {
      public void run()
      {
        frame.setTitle(newTitle);        
      }      
    });    
  }

  public static void main(String[] args) throws IOException
  {
    InfinitestMain app = new InfinitestMain(new File(args[0]));
    app.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
    app.frame.setVisible(true);
  }

  public synchronized void waitForIdle() throws InterruptedException
  {
    long time = System.currentTimeMillis();
    while (updateRunning && System.currentTimeMillis() - time < 4999)
      wait(5000);
  }
}
